package org.example.websockettest.websocket;

import org.example.websockettest.service.impl.UserServiceImpl;
import org.example.websockettest.service.interfaces.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class WebSocketEventListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketEventListener.class);

    @Autowired
    private RedisService redisService;
    @Autowired
    private UserServiceImpl userService;

    private final ConcurrentHashMap<String, String> sessionUsernameMap = new ConcurrentHashMap<>();

    @EventListener
    public void handleWebSocketConnectListener(SessionConnectedEvent event) {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        String sessionId = headerAccessor.getSessionId();
        String username = extractUsernameFromEvent(event.getMessage());

        if (username != null) {
            LOGGER.info("Received a new web socket connection: " + username);
            sessionUsernameMap.put(sessionId, username);
            redisService.addUser(username);
            userService.updateUserOnlineStatus(username, true);
        } else {
            LOGGER.warn("Web socket connection received without username.");
        }
    }

    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        String sessionId = headerAccessor.getSessionId();
        String username = sessionUsernameMap.remove(sessionId);

        if (username != null) {
            LOGGER.info("User disconnected: " + username);
            redisService.removeUser(username);
            userService.updateUserOnlineStatus(username, false);
        } else {
            LOGGER.warn("Web socket disconnection received without username.");
        }
    }

    private String extractUsernameFromEvent(Message<?> message) {
        MessageHeaders headers = message.getHeaders();
        if (headers == null) {
            LOGGER.warn("Message headers are null");
            return null;
        }

        LOGGER.debug("Message headers: " + headers);

        Object simpConnectMessageObj = headers.get("simpConnectMessage");
        if (simpConnectMessageObj == null) {
            LOGGER.warn("simpConnectMessage is null");
            return null;
        }

        LOGGER.debug("simpConnectMessage: " + simpConnectMessageObj);

        Map<String, List<String>> nativeHeaders = null;
        if (simpConnectMessageObj instanceof Message) {
            Message<?> simpConnectMessage = (Message<?>) simpConnectMessageObj;
            nativeHeaders = (Map<String, List<String>>) simpConnectMessage.getHeaders().get("nativeHeaders");
        } else if (simpConnectMessageObj instanceof Map) {
            nativeHeaders = (Map<String, List<String>>) ((Map<?, ?>) simpConnectMessageObj).get("nativeHeaders");
        }

        if (nativeHeaders == null) {
            LOGGER.warn("nativeHeaders are null");
            return null;
        }

        LOGGER.debug("nativeHeaders: " + nativeHeaders);

        List<String> usernameList = nativeHeaders.get("username");
        if (usernameList == null || usernameList.isEmpty()) {
            LOGGER.warn("usernameList is null or empty");
            return null;
        }

        return usernameList.get(0);
    }
}
